package com.deng.template.common.unit;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Deng
 * @describe 时间工具类
 * @date 2022/11/1
 */
@Slf4j
public class DateUtils {
    private DateUtils() {
    }

    public static final String DATEPATTERN_MM_DD_HH_MM = "MM-dd HH:mm";

    public static final String DATEPATTERN_MM_DD_HH_MM_SS = "MM-dd HH:mm:ss";

    public static final String DATEPATTERN_DATE_SLANT_TIME_COLON = "yyyy/MM/dd HH:mm:ss";

    public static final String DAY_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String DAY_FORMAT = "yyyy-MM-dd";

    public static final String DAY_FORMAT_YYYYMMDD = "yyyyMMdd";

    public static final String DAY_FORMAT_2 = "yyyy/MM/dd";

    public static final String DAY_FORMAT_YYYY_MM = "yyyy-MM";

    public static final String TIME_FORMAT = "HH:mm";

    public static final String TIME_FORMAT_HH_MM_SS = "HH:mm:ss";

    public static final String DATE_PATTERN_DD_MM_YY_HH_MM = "ddMMyy HH:mm";

    public static final String DAY_FORMAT_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";

    public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";

    public static final String DATE_FORMAT_YYYY_MM_DD_HH_MM_SS = "yyyyMMddHHmmss";

    public static final String FLIGHT_DATE = "ddMMMyy";

    public static final String FLIGHT_DATE_TIME = "ddMMMyy HH:mm";

    public static final String FLIGHT_DATE_FULL_TIME = "ddMMMyy HH:mm:ss";

    public static final String FLIGHT_DATE_TIME_HH_MM_DDMMM = "HH:mm ddMMM";

    public static final String DAY_FORMAT_HH_MM_DD_MM_YYYY = "HH:mm dd-MM-yyyy";

    public static final String DAY_FORMAT_DDMMM = "ddMMM";

    public static final String DAY_FORMAT_MMMDD = "MMMdd";

    public static final Map<Integer, String> DAY_MAP = new HashMap<>();

    /**
     * 日-月-年
     */
    public static final String DATE_REX = "([0-9]{1,2})([A-Z]{3})([0-9]{2,4})\\s?([0-9]{1,2}:[0-9]{1,2})?";

    public static final Pattern DATE_PATTERN = Pattern.compile(DATE_REX);

    /**
     * 月份英文缩写、数字的映射
     */
    private static final Map<String, String> MONTH_MAP = new HashMap<>();

    /**
     * 数字3
     */
    private static final int NUMBER_THREE = 3;

    /**
     * 数字4
     */
    private static final int NUMBER_FOUR = 4;

    static {

        DAY_MAP.put(-1, "P");
        DAY_MAP.put(0, "");
        DAY_MAP.put(1, "N");
        DAY_MAP.put(2, "S");
        DAY_MAP.put(3, "T");
        DAY_MAP.put(4, "A");
        DAY_MAP.put(5, "B");
        DAY_MAP.put(6, "C");
        DAY_MAP.put(7, "D");
        DAY_MAP.put(8, "E");
        DAY_MAP.put(9, "F");
        DAY_MAP.put(10, "G");
        DAY_MAP.put(11, "H");
        DAY_MAP.put(12, "I");
        DAY_MAP.put(13, "J");
        DAY_MAP.put(14, "K");
        DAY_MAP.put(15, "L");

        MONTH_MAP.put("JAN", "01");
        MONTH_MAP.put("FEB", "02");
        MONTH_MAP.put("MAR", "03");
        MONTH_MAP.put("APR", "04");
        MONTH_MAP.put("MAY", "05");
        MONTH_MAP.put("JUN", "06");
        MONTH_MAP.put("JUL", "07");
        MONTH_MAP.put("AUG", "08");
        MONTH_MAP.put("SEP", "09");
        MONTH_MAP.put("OCT", "10");
        MONTH_MAP.put("NOV", "11");
        MONTH_MAP.put("DEC", "12");
        MONTH_MAP.put("一月", "1");
        MONTH_MAP.put("二月", "2");
        MONTH_MAP.put("三月", "3");
        MONTH_MAP.put("四月", "4");
        MONTH_MAP.put("五月", "5");
        MONTH_MAP.put("六月", "6");
        MONTH_MAP.put("七月", "7");
        MONTH_MAP.put("八月", "8");
        MONTH_MAP.put("九月", "9");
        MONTH_MAP.put("十月", "10");
        MONTH_MAP.put("十一月", "11");
        MONTH_MAP.put("十二月", "12");
    }

    /**
     * 字符串转成日期
     *
     * @param date   时间字符串
     * @param format 转换格式
     * @return 日期
     */
    public static Date stringToDate(String date, String format) {
        if (StringUtils.isBlank(date)) {
            return null;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        try {
            return simpleDateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 日期转成字符串
     *
     * @param date   日期
     * @param format 转换格式
     * @return 日期字符串
     */
    public static String dateToString(Date date, String format) {
        if (date == null) {
            return null;
        }
        try {
            Format dateFormat = new SimpleDateFormat(format);
            return dateFormat.format(date);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            return null;
        }
    }

    /**
     * 获取一天的开始时间
     *
     * @param date 日期
     * @return 开始时间
     */
    public static Date getDayBegin(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 在指定日期上增加时间-分钟
     *
     * @return java.util.Date
     */
    public static Date addMinutes(Date date, int amount) {
        return add(date, Calendar.MINUTE, amount);
    }

    /**
     * 在指定日期上增加时间-秒
     *
     * @return java.util.Date
     */
    public static Date addSecond(Date date, int amount) {
        return add(date, Calendar.SECOND, amount);
    }

    /**
     * 在指定日期上增加时间-小时
     *
     * @return java.util.Date
     */
    public static Date addHours(Date date, int amount) {
        return add(date, Calendar.HOUR_OF_DAY, amount);
    }

    /**
     * 在指定日期上增加时间
     *
     * @return java.util.Date
     */
    public static Date add(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }

    /**
     * 时间加上指定的天数
     *
     * @param date
     * @param addDays
     * @return
     */
    public static Date addDays(Date date, int addDays) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, addDays);
        return calendar.getTime();
    }

    /**
     * 获取一天的结束时间
     *
     * @param date 日期
     * @return 结束时间
     */
    public static Date getDayEnd(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 59);
        return calendar.getTime();
    }

    /**
     * @param date
     * @param showTime
     * @param timeZone
     * @param pattern
     * @return
     */
    public static String dateToEng(Date date, boolean showTime, TimeZone timeZone, String pattern) {
        if (date == null) {
            return null;
        }
        if (showTime) {
            return DateFormatUtils.format(date, pattern, timeZone, Locale.ENGLISH).toUpperCase(Locale.ENGLISH);
        }
        return DateFormatUtils.format(date, pattern, timeZone, Locale.ENGLISH).toUpperCase(Locale.ENGLISH);
    }

    /**
     * 通过时间秒毫秒数判断两个时间的间隔
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDaysByTime(Date date1, Date date2) {
        SimpleDateFormat ds = new SimpleDateFormat(DAY_FORMAT);
        Date parse2 = null;
        try {
            parse2 = ds.parse(ds.format(date2));
        } catch (Exception e) {

        }
        return (int) ((parse2.getTime() - date1.getTime()) / (1000 * 3600 * 24));
    }

    /**
     * 获取时区,如GMT+8
     *
     * @param timeZone
     * @return
     */
    public static String getTimeZone(String timeZone) {
        Date date = new Date();
        Date dateByTimeZone = transferDateByTimeZone(date, TimeZone.getTimeZone(ZoneId.of(timeZone)));
        int result = 8 - ((int) (date.getTime() / 1000) - (int) (dateByTimeZone.getTime() / 1000)) / 3600;
        return "GMT+" + result;
    }

    /**
     * 将Date类型的时间转化成对应时区的Date类型的时间
     *
     * @param date
     * @param timeZone
     * @return
     */
    public static Date transferDateByTimeZone(Date date, TimeZone timeZone) {
        if (date == null) {
            return null;
        }
        String format = DateFormatUtils.format(date, DAY_TIME_FORMAT, timeZone);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DAY_TIME_FORMAT);
        Date parse = null;
        try {
            parse = simpleDateFormat.parse(format);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return parse;
    }

    /**
     * 时间转换成日期格式
     *
     * @param date
     */
    public static Date parseToDate(Date date) {
        if (date == null) {
            return null;
        }

        SimpleDateFormat df = new SimpleDateFormat(DAY_TIME_FORMAT);
        SimpleDateFormat ds = new SimpleDateFormat(DAY_FORMAT);
        try {
            return ds.parse(df.format(date));
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 转换成航班的时间 ddMMyy HH:mm MM为英文
     *
     * @param date
     * @param showTime
     * @return
     */
    public static String dateToStr(Date date, boolean showTime, TimeZone timeZone, String pattern) {
        if (date == null) {
            return null;
        }
        if (showTime) {
            return DateFormatUtils.format(date, pattern, timeZone, Locale.ENGLISH).toUpperCase(Locale.ENGLISH);
        }
        return DateFormatUtils.format(date, pattern, timeZone, Locale.ENGLISH).toUpperCase(Locale.ENGLISH);
    }

    /**
     * 时间加上指定的几个月
     *
     * @param date
     * @param addMonths
     * @return
     */
    public static Date addMonth(Date date, int addMonths) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, addMonths);
        return calendar.getTime();
    }

    /**
     * 获取近几个月时间
     *
     * @param before 提前月份
     * @return 近几个月的第一天
     */
    public static Date getBeforeMonthStart(int before) {
        LocalDateTime now = LocalDateTime.now().plusMonths(before);
        return new Date(LocalDateTime.of(now.getYear(), now.getMonthValue(), 1, 0, 0, 0).toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }

    /**
     * 获取日期对应月的最后一天
     * 获取指定日期小时的最后一秒 去除分钟和秒
     *
     * @param date
     * @return
     */
    public static Date getDayHourEnd(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * h获取指定日期 去除分钟和秒
     *
     * @param date
     * @return
     */
    public static Date getDayHourStart(Date date) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取月的第一天
     *
     * @param date 日期
     * @return 日期
     */
    public static Date getMonthStart(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取当月第一天
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 获取月的最后一天
     *
     * @param date 日期
     * @return 日期
     */
    public static Date getMonthEnd(Date date) {
        if (date == null) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        // 获取当月最后一天
        int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, maxDay);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 59);
        return calendar.getTime();
    }

    /**
     * 日期转换
     *
     * @param date     日期
     * @param format   格式
     * @param timeZone 时区
     * @return 日期
     */
    public static Date stringToDate(String date, String format, TimeZone timeZone) {

        if (StringUtils.isBlank(date)) {
            return null;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        try {
            simpleDateFormat.setTimeZone(timeZone);
            return simpleDateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解析航班日期
     *
     * @param flightDate
     * @return
     */
    public static Date parseRclFlightDate(String flightDate) {
        if (StringUtils.isBlank(flightDate)) {
            return null;
        }
        Matcher matcher = DATE_PATTERN.matcher(flightDate);
        if (matcher.find()) {
            int count = matcher.groupCount();
            if (count >= NUMBER_THREE) {
                String day = matcher.group(1);
                String month = matcher.group(2);
                String year = matcher.group(3);

                if (count >= NUMBER_FOUR) {
                    String hhmm = matcher.group(4);
                    if (StringUtils.isNotBlank(hhmm)) {
                        hhmm = StringUtils.remove(hhmm, ":");
                    }
                    return parseDateByRclFlight(year, month, day, hhmm);
                }

                return parseDateByRclFlight(year, month, day, null);
            }

        }
        return null;
    }

    /**
     * 根据月份、日期等转换成日期
     *
     * @param year      年
     * @param monthAbbr 月份字符串，JAN、FEB
     * @param dateStr   日期字符串，01、02
     * @param hhmmStr   小时分钟字符串，1330
     * @return
     */
    public static Date parseDateByRclFlight(String year, String monthAbbr, String dateStr, String hhmmStr) {

        if (StringUtils.isBlank(monthAbbr)) {
            throw new RuntimeException("parseDate error, monthAbbr[" + monthAbbr + "] is not correct month");
        }

        Calendar calendar = Calendar.getInstance();
        // 年
        if (StringUtils.isNotBlank(year)) {
            year = StringUtils.leftPad(year, 4, "20");
            calendar.set(Calendar.YEAR, Integer.valueOf(year));
        }
        // 1.月份，0代表1月
        //兼容报文里存在非大写情况
        monthAbbr = StringUtils.upperCase(monthAbbr);

        String monthStr = MONTH_MAP.get(monthAbbr);
        try {
            calendar.set(Calendar.MONTH, Integer.parseInt(monthStr) - 1);
        } catch (Exception e) {
            throw new RuntimeException("parseDate error, monthAbbr[" + monthAbbr + "] is not correct hhmm");
        }
        // 2.日期
        if (StringUtils.isBlank(dateStr)) {
            calendar.set(Calendar.DATE, 1);
        } else {
            int date;
            try {
                date = Integer.parseInt(dateStr);
            } catch (Exception e) {
                throw new RuntimeException("parseDate error, dateStr[" + dateStr + "] is not correct date");
            }
            calendar.set(Calendar.DATE, date);
        }
        // 3.小时、分钟
        if (StringUtils.isBlank(hhmmStr)) {
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
        } else {
            if (hhmmStr.trim().length() != NUMBER_FOUR) {
                throw new RuntimeException("parseDate error, hhmmStr[" + hhmmStr + "] is not correct hhmm");
            }
            int hour;
            int minute;

            try {
                hour = Integer.parseInt(hhmmStr.substring(0, 2));
                minute = Integer.parseInt(hhmmStr.substring(2, 4));
            } catch (Exception e) {
                throw new RuntimeException("parseDate error, hhmmStr[" + hhmmStr + "] is not correct hhmm");
            }
            calendar.set(Calendar.HOUR_OF_DAY, hour);
            calendar.set(Calendar.MINUTE, minute);
        }
        // 4.秒、毫秒
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 取两个日期间隔多少分钟
     */
    public static Long intervalMinutes(Date endDate, Date startDate) {
        //从对象中拿到时间
        SimpleDateFormat df = new SimpleDateFormat(DAY_TIME_FORMAT);
        try {
            long d1 = df.parse(df.format(endDate)).getTime();
            long d2 = df.parse(df.format(startDate)).getTime();
            return (d1 - d2) / 1000 / 60;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Integer intervalHour(Date endDate, Date startDate) {
        //从对象中拿到时间
        long dayM = 1000 * 24 * 60 * 60;
        long hourM = 1000 * 60 * 60;
        long differ = endDate.getTime() - startDate.getTime();
        long hour = differ % dayM / hourM + 24 * (differ / dayM);
        return Integer.parseInt(String.valueOf(hour));
    }

    /**
     * 减少
     *
     * @param startDate
     * @param day
     * @return
     */
    public static Date addDay(Date startDate, int day) {
        if (startDate == null) {
            return null;
        } else {
            Calendar cal = Calendar.getInstance();
            cal.setTime(startDate);
            cal.add(5, day);
            return cal.getTime();
        }
    }

    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);

        return date.after(begin) && date.before(end);
    }


}


